package org.geektimes.configuration.microprofile.config;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigValue;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.Converter;
import org.geektimes.configuration.microprofile.config.converters.Converters;
import org.geektimes.configuration.microprofile.config.sources.ConfigSources;

public class DefaultConfig implements Config {

	private ConfigSources configSources;

	private Converters converters;

	public DefaultConfig(ConfigSources configSources, Converters converters) {
		this.configSources = configSources;
		this.converters = converters;
	}

	@Override
	public <T> T getValue(String propertyName, Class<T> propertyType) {
		String value = getPropertyValue(propertyName);
		if( value != null ) {
			Converter<T> converter = doGetConverter(propertyType);
			if( converter != null ) {
				return converter.convert(value);
			}else {
				throw new RuntimeException("no compatible converter");
			}
		}
		
		return null;
	}

	@Override
	public ConfigValue getConfigValue(String propertyName) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public <T> Optional<T> getOptionalValue(String propertyName, Class<T> propertyType) {
		T value = getValue(propertyName, propertyType);
		return Optional.ofNullable(value);
	}

	@Override
	public Iterable<String> getPropertyNames() {
		List<String> propertyNames = new ArrayList<String>();
		for (ConfigSource configSource : configSources) {
			propertyNames.addAll(configSource.getPropertyNames());
		}
		return Collections.unmodifiableList(propertyNames);
	}

	@Override
	public Iterable<ConfigSource> getConfigSources() {
		return this.configSources;
	}

	@Override
	public <T> Optional<Converter<T>> getConverter(Class<T> forType) {
		Converter<T> converter = doGetConverter(forType);
		return Optional.ofNullable(converter);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public <T> Converter<T> doGetConverter(Class<T> forType) {
		List<Converter> converters = this.converters.getConverters(forType);
		return converters.isEmpty() ? null : converters.get(0);
	}

	@Override
	public <T> T unwrap(Class<T> type) {
		if (type.isAssignableFrom(Config.class)) {
			type.cast(this);
		}
		throw new IllegalArgumentException("unsupport type");
	}

	protected String getPropertyValue(String propertyName) {
		String propertyValue = null;
		for (ConfigSource configSource : configSources) {
			propertyValue = configSource.getValue(propertyName);
			if (propertyValue != null) {
				break;
			}
		}
		return propertyValue;
	}

}
